<?php
/**
 * SleekPHP 0.1
 *
 * A Lightweight, Open Source and Modular
 * PHP Web Development Framework Built on the MVC Pattern
 *
 * SleekPHP is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * SleekPHP is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * @author Andreas Lagerkvist <powerbuoy@gmail.com>
 * @copyright Copyright (c) 2010-2011 Andreas Lagerkvist
 * @license http://opensource.org/licenses/gpl-license.php GNU Public License
 * @version 0.1
 */
# Some config that's rarely changed
define('HOME_PAGE',		'Home');			# The home/index page
define('LOGIN_PAGE',	'Login');			# The login page
define('FOF_PAGE',		'FourOFour');		# The 404 page
define('NOT_AUTH_PAGE',	'NotAuthorized');	# "Not authorized" page

# Authorization levels
define('AUTH_LEVEL_GUEST',	0);
define('AUTH_LEVEL_MEMBER',	1);
define('AUTH_LEVEL_ADMIN',	999);
define('AUTH_LEVEL_SU',		1000);

# Paths, session names etc
define('USER_SESSION', 'sleekphp_user');
define('DOCROOT', realpath(dirname(__FILE__) . '/../') . '/');						# Path to SleekPHP document root
define('WEBROOT', str_replace('//', '/', dirname($_SERVER['SCRIPT_NAME']) . '/'));	# Path to SleekPHP "web" root
define('XHR', (																		# Whether this is an ajax request or not
	isset($_SERVER['HTTP_X_REQUESTED_WITH']) and 
	strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest'
));

# Start logger first of all
require DOCROOT . 'Core/Logger.php';

Logger::init('Welcome to SleekPHP 0.1 :)');

# Enable sessions
session_start();

# Error reporting
if (DEBUG) {
	error_reporting(E_ALL);
	ini_set('display_errors', true);
}
else {
	error_reporting(0);
	ini_set('display_errors', false);
}

# Include Libs
require DOCROOT . 'Lib/Markdown.php';
require DOCROOT . 'Lib/SleekRouter.php';
require DOCROOT . 'Lib/Form.php';

# Include Core files
require DOCROOT . 'Core/AutoLoader.php';
require DOCROOT . 'Core/Config.php';
require DOCROOT . 'Core/DB.php';
require DOCROOT . 'Core/Events.php';
require DOCROOT . 'Core/ExceptionHandler.php';
require DOCROOT . 'Core/Functions.php';
require DOCROOT . 'Core/Lang.php';
require DOCROOT . 'Core/Model.php';
require DOCROOT . 'Core/Module.php';
require DOCROOT . 'Core/Page.php';
require DOCROOT . 'Core/Router.php';
require DOCROOT . 'Core/Site.php';
require DOCROOT . 'Core/Style.php';
require DOCROOT . 'Core/User.php';

Logger::log('Core files included.', __FILE__);

# Set autoloader + exception handler
set_exception_handler('ExceptionHandler::handleException');
spl_autoload_register('AutoLoader::load');

/**
 * Wrapper function for SleekPHP::router()->urlFor()
 * retrieves the URL for $for based on potential $data
 *
 * @param string $for
 * @param array|boolean $data
 */
function url ($for, $data = false) {
	return SleekPHP::router()->urlFor($for, $data);
}

/**
 * Wrapper function for SleekPHP::lang()->translate()
 * Translates $str based on $data and _might_ escape HTML ($escHTML)
 *
 * @param string $str
 * @param array|boolean $data
 * @param boolean $escHTML
 */
function t ($str, $data = false, $escHTML = true) {
	return SleekPHP::lang()->translate($str, $data, SleekPHP::currentLang(), $escHTML);
}

/**
 * Wrapper function for SleekPHP::config()->get()
 * Retrieves one config key's value based on $key
 *
 * @param string $key
 */
function conf ($key) {
	return SleekPHP::config()->get($key);
}


/**
 * Wrapper function for SleekPHP::user()
 */
function User () {
	return SleekPHP::user();
}

/**
 * The main framework class which ties everything together
 * also holds instances of things needed globally like DB, Router, Config etc
 *
 * @subpackage Core
 */
class SleekPHP {
	/**
	 * associative array of ALL module data
	 *	'Base' => array(
	 *		'html_title'	=> 'Home', 
	 *		'body_id'		=> 'home-page'
	 *	),
	 *	'Page' => array(
	 *		'content'		=> 'Lorem ipsum dolor...'
	 *	)
	 *
	 * @var array $data
	 */
	private static $data;

	/**
	 * Current request (ie /about/)
	 *
	 * @var string $request
	 */
	private static $request;

	/**
	 * The current page (ie "About") (based on request)
	 *
	 * @var string $currentPage
	 */
	public static $currentPage;

	/**
	 * The current style (based on cookies or config)
	 *
	 * @var string $currentStyle
	 */
	private static $currentStyle;

	/**
	 * Current language (ie "en") (based on request)
	 *
	 * @var string $currentLang
	 */
	private static $currentLang;

	/**
	 * Instance of Site (the site we're going to run)
	 *
	 * @var Site $site
	 */
	private static $site;

	/**
	 * Array of Module instances for each module that has been run on this page
	 *
	 * @var array $modules
	 */
	public static $modules;

	/**
	 * Instance of Router-class (based on this $site)
	 *
	 * @var Router $router
	 */
	private static $router;

	/**
	 * Instance of Config-class (based on this $site)
	 *
	 * @var Config $config
	 */
	private static $config;

	/**
	 * Instance of Lang (the current $site's lang)
	 *
	 * @var Lang $lang
	 */
	private static $lang;

	/**
	 * Instance of DB-class
	 *
	 * @var DB $db
	 */
	private static $db;

	/**
	 * Instance of User-class (the current visitor)
	 *
	 * @var User $user
	 */
	private static $user;

	/**
	 * Creates all the classes we need an initiates the framework
	 * then runs the $site specified
	 *
	 * @param string $site
	 */
	public static function run ($site, $request) {
		# Create the site and its routes, config and lang, set up a DB and a user object
		self::$site		= new Site($site);
		self::$config	= new Config(self::$site->getConfigs());
		self::$lang		= new Lang(self::$site->getLangs());
		self::$db		= new DB(DOCROOT . 'Sites/' . self::$site->getSite() . '/' . self::$site->getSite() . '.db');
		self::$user		= new User();

		Logger::log('Core classes created.', __METHOD__);

		# Work out the request as well as the requested lang
		self::setRequestAndCurrentLang($request);

		# Needs to run after request is determined
		self::$router = new Router(self::$site->getRoutes());

		Logger::log('Requested URI "' . self::$request . '" and language "' . self::$currentLang . '" determined.', __METHOD__);

		# UTF8 and text/html
		if (setlocale(LC_ALL, self::$lang->lc2cc(self::$currentLang) . '.UTF8')) {
			Logger::log('Locale set to "' . self::$lang->lc2cc(self::$currentLang) . '".', __METHOD__);
		}
		else {
			Logger::log('Could not change locale to "' . self::$lang->lc2cc(self::$currentLang) . '".', __METHOD__);
		}

		header('Content-Type: text/html; charset=utf-8');

		Logger::log('UTF8 header and DB names set.', __METHOD__);

		# Work out the style to use for this site
		self::setCurrentStyle();

		Events::trigger('BeforeRun');

		# TODO: A single module is requested (should support several modules)
		if (($module = self::$router->getModule())) {
			$output = self::$site->renderModules(array($module), !XHR, array_merge(self::$router->getParams(self::$request), $_GET));
		}
		# An entire page is requested
		elseif (($page = self::$router->getPage(self::$request))) {
			$output = self::$site->renderPage($page, self::$router->getParams(self::$request));
		}
		# The request didn't match either a page or a module
		else {
			header('HTTP/1.1 404 Not Found');

			self::$currentPage = FOF_PAGE;

			if (self::$site->hasPage(FOF_PAGE)) {
				$output = self::$site->renderPage(FOF_PAGE);
			}
			else {
				$output = '404 - no route';
			}
		}

		return Events::trigger('BeforeOutput', $output);
	}

	/**
	 * Gets data for a particular $module's $key
	 * if $key is false, gets all module data
	 *
	 * @param string $module
	 * @param string $key
	 */
	public static function getData ($module, $key = false) {
		# Get one data value
		if ($key) {
			return isset(self::$data[$module][$key]) ? self::$data[$module][$key] : null;
		}
		# Get all data for module
		else {
			return isset(self::$data[$module]) ? self::$data[$module] : null;
		}
	}

	/**
	 * Sets data for a particular $module's $key
	 *
	 * @param string $module
	 * @param string $key
	 * @param mixed $val
	 */
	public static function setData ($module, $key, $val) {
		self::$data[$module][$key] = $val;
	}

	/**
	 * Uses $request to determine the actual request and current lang
	 * redirects to / if user tries to access default lang through /default_lang/
	 */
	private static function setRequestAndCurrentLang ($request) {
		$allowedLangs	= conf('lang.allowed_langs');
		$defaultLang	= conf('lang.default_lang');
		$defaultLang	= $defaultLang ? $defaultLang : 'en';
		$currentLang	= $defaultLang;

		if (is_array($allowedLangs) and count($allowedLangs)) {
			$requestedLang = tunnArray(explode('/', $request));
			$requestedLang = count($requestedLang) ? $requestedLang[1] : false;

			# See if first URI portion (/en/ for example) is one of our allowed langs
			if (in_array($requestedLang, $allowedLangs)) {
				# If URI points to default lang, redirect it away
				if ($requestedLang == $defaultLang) {
					redirect(str_replace('/' . $defaultLang . '/', '/', currPageURL()));
				}

				$currentLang = $requestedLang;
			}
		}

		self::$request		= ($currentLang == $defaultLang) ? $request : str_replace(array("/$currentLang/", "/$currentLang"), '/', $request);
		self::$currentLang	= $currentLang;
	}

	/**
	 * Updates self::$currentStyle as well as the base module's variables
	 */
	public static function setStyle ($style) {
		if (SleekPHP::site()->hasStyle($style)) {
			self::$currentStyle = $style;

			$currSite = SleekPHP::site()->getSite();

			self::setData('Base', 'css', SleekPHP::router()->urlForStyle($currSite, $style));
			self::setData('Base', 'js_foot', SleekPHP::router()->urlForStyle($currSite, $style, 'js'));
			self::setData('Base', 'js_head', SleekPHP::router()->urlForStyle($currSite, $style, 'head.js'));
		}
		else {
			throw new Exception("The requested style \"$style\" could not be found in your site (" . SleekPHP::site()->getSite() . ")<br/>This style may have been set by one of your extended site's config files.<br/>Make sure you configure general.default_style to a style that exists in your site.");
		}
	}

	/**
	 * Checks if the user has a preferred style that is valid
	 * if not falls back to config.default_style
	 */
	private static function setCurrentStyle () {
		# See if user wants to change style
		$tmp = array_merge($_GET, $_POST);

		if (isset($tmp['style']) and SleekPHP::site()->hasStyle($tmp['style'])) {
			self::$currentStyle = basename($tmp['style']);

			setcookie('sleekphp_style', basename($tmp['style']), time() + 31536000, WEBROOT);
		}
		else {
			if (isset($_COOKIE['sleekphp_style']) and SleekPHP::site()->hasStyle($_COOKIE['sleekphp_style'])) {
				self::$currentStyle = basename($_COOKIE['sleekphp_style']);
			}
			else {
				self::$currentStyle = conf('general.default_style');
			}
		}

		self::setStyle(self::$currentStyle);
	}

	/**
	 * Returns the current request
	 */
	public static function request () {
		return self::$request;
	}

	/**
	 * Returns the current page
	 */
	public static function currentPage () {
		return self::$currentPage;
	}

	/**
	 * Returns the current style
	 */
	public static function currentStyle () {
		return self::$currentStyle;
	}

	/**
	 * Returns the current language
	 */
	public static function currentLang () {
		return self::$currentLang;
	}

	/**
	 * Returns an instance of the current site
	 */
	public static function site () {
		return self::$site;
	}

	/**
	 * Returns an instance of the router
	 */
	public static function router () {
		return self::$router;
	}

	/**
	 * Returns an instance of the current site's config
	 */
	public static function config () {
		return self::$config;
	}

	/**
	 * Returns an instance of the current site's lang
	 */
	public static function lang () {
		return self::$lang;
	}

	/**
	 * Returns an instance of the db
	 */
	public static function db () {
		return self::$db;
	}

	/**
	 * Returns an instance of the current user / visitor
	 */
	public static function user () {
		return self::$user;
	}
}
